/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.creator.logical.links.сore;

import java.io.File;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import ru.creator.logical.links.db.DB;
import ru.creator.logical.links.db.Table;
import ru.creator.logical.links.db.tables.Subjects;
import ru.creator.logical.links.db.tables.Words;
import ru.creator.logical.links.сore.util.Log;
import ru.creator.logical.links.db.TableObject;
import ru.creator.logical.links.db.tables.Links;


/**
 *
 * @author ~jo-MA-jo~
 */
public class Logic {
   private String dbPatch ="";
   private ConcurrentHashMap<String,DB> db;
   private ConcurrentHashMap<String,Words> words;
   private ConcurrentHashMap<String,Subjects> subjects;
   private ConcurrentHashMap<Integer,List<Links>> links;
   private ConcurrentHashMap<String,Links> links_subjects;
   private IMessageListener ml;
   private Log logger;
   private int wordsCount=0;
   private MessageViewer mv;
   
   
   public Logic(IMessageListener ml, String dbPatch) {
       logger = new Log();
       this.dbPatch = (dbPatch.endsWith(File.separator)) ? dbPatch : dbPatch+File.separator;
       db = new ConcurrentHashMap<String,DB>();
       words = new ConcurrentHashMap<String,Words>();
       subjects = new ConcurrentHashMap<String,Subjects>();
       links = new ConcurrentHashMap<Integer,List<Links>>();
       links_subjects = new ConcurrentHashMap<String,Links>();
       this.ml = ml;
       initDb();
       fillCash();
       mv = new MessageViewer(this);
   }
   
   /**
    * возвращает logger
    * @return logger
    */
   public Log getLogger(){
       return logger;
   }

   /**
    * возвращает MessageViewer
    * @return MessageViewer
    */
   public MessageViewer getMessageViewer(){
       return mv;
   }   
   /**
    * инициируюет базы
    */
   private void initDb(){
       try {
             db.put("words",getDB ("words.db"));
             db.put("subjects",getDB ("subjects.db"));
             db.put("links",getDB ("links.db"));
        } catch (Exception ex) {
            logger.err(ex.getMessage());
        }
       
   }

   /**
    * подключает базу
    */
   private DB getDB (String file){
       DB temp_db = null;
       try {
             File file_db = new File(dbPatch+file);
             temp_db = new DB(logger);
             temp_db.openConnection(file_db);
        } catch (Exception ex) {
            logger.err(ex.getMessage());
        }
       return temp_db;
   }   

   /**
    * возвращает подключённую базу
    * @param name_db имя базы
    * @return
    */
   private DB getConnectionDB (String name_db){
       if (db.containsKey(name_db))
           return db.get(name_db);
       else
           return null;
   }
   
   /**
    * добавляет слушатель сообщений
    * @param ml
    */
   public void addMessageListener(IMessageListener ml){
        this.ml = ml;
   }   

   /**
    * удаляет слушатель сообщений
    * @param ml
    */
   public void removeMessageListener(IMessageListener ml){
        this.ml = null;
   }   

   public IMessageListener getMessageListener(){
        return ml;
   }
   /**
    * кэширует базы
    */
   private void fillCash(){
       fillCashW();
       fillCashS();
      // System.out.println("[essence] - Пришел в сознание ;) ");
       logger.out("Пришел в сознание ;)");
   }

    /**
     * Заполняет кэш субъектов из БД
     */
    private void fillCashS() {
        String q = "select * from subjects ORDER BY id ASC";
        ResultSet rst=null;
        Statement stmt = null;
        try{
                stmt = db.get("subjects").getDb().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
                rst = stmt.executeQuery(q);
                Subjects s = new Subjects();
        	while(rst.next()){
                        s.id = rst.getInt(1);
                        s.subject= rst.getString(2);
                        byte[] data = rst.getBytes(3);
                        s.landmark = (Set<Integer>) TableObject.toObject(data);
                        s.init();
                        subjects.put(rst.getString(2), s);
        	}
        } catch (Exception ex){
            logger.err(ex.getMessage());
        } finally {
        	if(rst!=null) try{rst.close();} catch(Exception e) {};
        	if(stmt!=null) try{stmt.close();} catch(Exception e) {};
        }
        //System.out.println("[essence] - Сache subjects OK!");
        logger.out("Сache subjects OK!");
    }

    /**
     * Заполняет кэш слов из БД
     */
    private void fillCashW() {
        String q = "select * from words ORDER BY id ASC";
        ResultSet rst=null;
        Statement stmt = null;
        try{
                stmt = db.get("words").getDb().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
                rst = stmt.executeQuery(q);
                Words w = new Words();
        	while(rst.next()){
        		w.id=rst.getInt(1);
        		w.word=rst.getString(2);
                        w.init();
                        words.put(rst.getString(2), w);
         	}
                wordsCount = w.id;
        } catch (Exception ex){
            logger.err(ex.getMessage());
        } finally {
        	if(rst!=null) try{rst.close();} catch(Exception e) {};
        	if(stmt!=null) try{stmt.close();} catch(Exception e) {};
        }
        //System.out.println("[essence] - Сache words OK!");
        logger.out("Сache words OK!");
    }
    
    /**
     * возвращает лист связей по слову
     * @param idWord ид слова
     * @return List<Links>
     */ 
    private List<Links> getListLinks(int idWord) {
        if (links.containsKey(idWord))return links.get(idWord);
        List list = db.get("links").getObjectList(new Links(),"select * from links where id="+idWord);
        if (list.size()>0)links.put(idWord, list);
        return list;
    } 
    
    /**
     * возвращает связи по слову и субъекту
     * @param idWord ид слова
     * @param subject ид субъекта
     * @return Links
     */
    private Links getLinks(int idWord,int subject) {
        if (links_subjects.containsKey(idWord+"_"+subject))return links_subjects.get(idWord+"_"+subject);
        Links l = (Links) db.get("links").getObject(new Links(),"select * from links where id="+idWord+" and subject ="+subject);
        if (l.id>0)links_subjects.put(idWord+"_"+subject, l);
        return l;
    }  
    
    /**
     * добавляет связь
     * @param idWord ид слова
     * @param subject ид субъекта
     * @param link ид связи
     */
    private void addLink(int idWord,int subject,int link ){
        Links l = getLinks(idWord,subject);
        Set<Integer> lnk = l.link;
        if (l.id!=0){
            if(!lnk.contains(link)){
                lnk.add(link);
                l.link = lnk;
                l.init();//не забывать обновлять после изменений
                db.get("links").updateObject(l);
                logger.db("Update link: ["+l.id+" : "+l.link+"]");
                links_subjects.put(idWord+"_"+subject, l);    
            }
        }else{
            lnk.add(link);
            l.link = lnk;
            l.id = idWord;
            l.init();//не забывать обновлять после изменений
            db.get("links").insertObject(l); 
            logger.db("Add new link: ["+l.id+" : "+l.link+"]");
            links_subjects.put(idWord+"_"+subject, l);    
        }
    }    

    /**
     * добавляет связь с субъектом по умолчанию
     * @param idWord ид слова
     * @param link ид связи
     */    
    private void addLink(int idWord,int link){
        addLink(idWord,0,link);
    }

    private synchronized int addWord(Words w) {
       // if (!words.containsKey(w.word)){
        w.id = wordsCount+1;
        w.init();//не забывать обновлять после изменений
        words.put(w.word, w);
        db.get("words").insertObject(w);
        logger.db("Add new word: ["+w.id+"] "+w.word);
        wordsCount=w.id;
        return w.id;
    }

    private synchronized void scanMessage (String Message){
        Message = Message.toLowerCase().trim();
        String w[] = Message.split(" ");
        for (String word: w){
            if (!words.containsKey(word))
                   addWord(new Words(word));
        }
    }

   /**
    * входящее сообщение
    * @param SenderID ид отправителя
    * @param Message сообщение
    */
   public void onIncomingMessage(String SenderID, String Message){
       if (ml!=null){
          scanMessage (Message);
          mv.addMessage(SenderID, Message.toLowerCase().trim());
//          OutMessageEvent ms = new OutMessageEvent(SenderID,"привет как дела?");
          logger.talkIn(SenderID+" - "+Message);
//          ml.onOutMessage(ms);
//          logger.talkOut(ms.getResponse());
       }else
          logger.err("Отсутствует слушатель сообщений! (MessageListener)");
   }

    

}
